home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: MegaDisc
/
MegaDisc 43 (1995-03)(MegaDisc Digital Publishing)(AU)(Disk 2 of 2)[m bamcopy].zip
/
MegaDisc 43 (1995-03)(MegaDisc Digital Publishing)(AU)(Disk 2 of 2)[m bamcopy].adf
/
Programming
/
Pascal_Tutes
/
Tute12.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-01-27
|
11KB
|
304 lines
{
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
«» «»
«» TUTORIAL TWELVE «»
«» «»
«»
by
«»
«» «»
«»
Anthony Peck
«»
«» «»
«» «»
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43
How many programmers does it take to change a global variable?
Time to 'fess up. That last tutorial was a bit of a dud. The reason?
We defined the variables at the start of the program, and then the
individual procedures had full access to all of them. For instance,
the procedure Getnumbers could change the variables Number and Result.
This is called a "side-effect" in programming, and it means that bug
hunting can be awfully difficult if each procedure can alter the
variables.
These "global" variables (defined at the outset of the program) should
be passed to the procedures, rather than letting the procedures just
snatch and grab by themselves. This way, we can control what
variables each procedure can alter.
For instance, let's take a program that takes a number from the user
and determines if it is even or odd. This will be such a world
shattering project it's likely we'll all end up with Nobel Prizes...
Forward ------------------>
}
Program MDTute12 (Input,Output);
{ This program will determine if a user entered
number is even or odd!
Author : A N Peck
Date : 26 October 1994
Procedures used:
Getnumber - prompts user for a number
Convertnumber - makes sure the user has entered a
positive whole number
EvenorOdd - determines if number is even or odd }
Var
userentry: real;
{
^
|
|__
Just to confuse the issue, we'll introduce a bit of
error checking. In this case, we'll allow the user to
enter in a real number (i.e. with a fractional
component) and convert it to an integer.
}
number: longint;
{ ---------------------------------------------------------- }
Procedure Getnumber (Var userchoice: real);
{
^
|
|__
Here's where the variable is passed to
the procedure. Because the variable is
being altered by the procedure, we prefix
the name of the variable with "Var".
The procedure variable name (
local
identifier
) is different from the global
variable name. This makes it much easier
to track a bug. We know that if anything
is wrong with "userchoice", the problem
must be in the procedure "Getnumber",
whereas if there is anything wrong with
"userentry", the bug must be in the main
program!
Looking at the main program below, the
value for "userchoice" should enter this
procedure with a value of 0.00. The user
will type in a new value, and it is this
altered variable which will be passed back
to the main program.
After the variable name comes the
variable type, which is "real"
}
{ Prompts user for a number }
begin
writeln;
write(' Please enter a whole number: ');
readln(userchoice);
end; { Getnumber }
{
^
|
|__
The variable "userchoice" exits the procedure and
is passed back to the main program as "userentry". The
main program will probably turn it into a nice little
teapot warmer.
}
{ ---------------------------------------------------------- }
Procedure Convertnumber
(realnumber: real; Var integernumber: longint);
{
^
|
|__
I've put the local identifiers and types on the next
line from the procedure heading to make the code look
sexy. What a concept!
Because we'll be converting the real number, we only need it
as an input, rather than a variable to change. Therefore, we
don't have to put the word "Var" before the variable name.
This number will not be changed by the procedure, it will be
converted! The variable being changed (integernumber) is
passed with the prefix "Var" because it will be altered.
Try leaving the "Var" out and running the program. You
should find that the number is always equal to 0, because
that is what we have initialised in the main program (see
below).
}
{ Converts a real number to a positive integer }
begin
integernumber := Abs(Round(realnumber));
{
^
|
|__
See Tute10.pas!
}
end; { Convertnumber }
{
^
|
|__
At the end of this procedure, realnumber is unchanged, but
"integernumber" is passed back to the main program as
"number", having being altered by the "Round" command. If
only plastic surgery was as easy as this, we'd all be
millionaires!
}
{ ---------------------------------------------------------- }
Procedure EvenorOdd (testnumber: longint);
{ Determines if an integer is even or odd }
begin
writeln;
If testnumber mod 2 = 0 then
{
^
|
|__
See Tute7.pas!
}
writeln(' ',testnumber,' is even!')
else
writeln(' ',testnumber,' is odd!');
writeln;
end; { EvenorOdd }
{ ---------------------------------------------------------- }
{ ---------------------------------------------------------- }
begin { Main Program }
userentry := 0.00;
number := 0;
{
^
|
|__
These two lines initialise the global variables. It ain't
strictly necessary in this case, but it's a damn fine way to
start a program and anyone reading your code will probably
want to shake your hand when they see this.
For big programs it's easier if the variable values are set by
you at the start so you can track them as they change. I've
just set these to zero (0.00 for the "real" variable to
distinguish it), but you could set them to any value within
the variable type range.
}
Getnumber(userentry);
{
^
|
|__
Here the global variable is passed to the
procedure. Note that when the procedure receives
the variable, the variable name is changed from the
global variable "userentry" to the local identifier
"userchoice". This makes it easier to track, and
also confuses any cat that thinks they might have a
crack at walking across your keyboard and coding an
Amiga version of "Chicago"®
}
Convertnumber(userentry,number);
{
^
|
|__
We're passing two variables here, one which is
just passed as input (userentry), and the other
which will be altered by the procedure and then
passed back as a different value (number).
The variables must be passed in the same order
that they are received by the procedure. Try
swapping them.
The variables must also be of the same type.
If "Getnumber" is expecting a real-type
variable, don't try to send it an integer-type
variable. Your compiler should pick up such an
error.
}
EvenorOdd(number);
end. { Main Program }
{ ---------------------------------------------------------- }
{
Spiritual! The whole main program consists of three lines, and
these just call the procedures and pass the global variables to them.
What a rort! Here's the spiffy bit; you can use each of the
procedures in other programs! For instance, now whenever you want to
convert a real number to an integer, just copy and paste the procedure
"Convertnumber" into your program and away you go! This is the real
beauty of procedures. Once you have determined that they are
operating according to your specifications, they are transportable.
No name changes are needed, because all the variables are defined
by local identifiers within the procedure. For "Convertnumber", all
you need to do is to send it a real number and an integer, and it will
send back the converted integer.
Let's say that we want a program which takes a number and finds out
if it is a multiple of three. We can use "Getnumber" and
"Convertnumber" from this program. All we need to do is to delete the
procedure "EvenorOdd" and insert another which will perform the
necessary calculations (called "Multiple"). Our new "Main Program"
would look something like this...
Getnumber(userentry);
Convertnumber(userentry,number);
Multiple(number);
Cool!
{
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
«» «»
«»
Duodenum elk pasta
«»
«» «»
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43 !¡ 43
}